# 背景
问题来源:如果使用 webpack,比如运行 npx webpack .
会在每次构建结束后控制台输出编译耗时。这时可能就会发出疑问,webpack 是如何计算编译时间的呢?
asset main.js 1.4 KiB [compared for emit] (name: main)
./index.js 56 bytes [built] [code generated]
./sum.js 41 bytes [built] [code generated]
webpack 5.74.0 compiled successfully in 75 ms
2
3
4
# 回答问题
- stats.endTime - stats.startTime
- stats.toJson().time
文章只对第一种方式获取到编译时间的过程进行了详述,另一种留给读者实践验证~
# 查看源码
# 思路
思路:从后往前分析,从结果出发,看调用堆栈
# 调试过程
说明:以下调试代码是使用 Webpack 的 Node.js API 实现,具体使用可参考官方文档。
https://webpack.docschina.org/api/node/ (opens new window)
https://webpack.docschina.org/api/node/#run (opens new window)
首先准备代码
build.js
const webpack = require('webpack')
const path = require('path')
function build1() {
return webpack({
entry: {
index: './index.js',
test: './test.js'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
},
mode: 'none'
})
}
build1()
.run((err, stat) => {
const startTime = stat.startTime
const endTime = stat.endTime
console.log("构建时间", endTime - startTime);
// console.log("构建时间", stat.endTime - stat.startTime);
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
index.js
console.log('index---');
test.js
function test() {
console.log('test---');
}
test()
2
3
4
5
开始调试
(1)因为我们知道 .run 内的回调能拿到 err 和 stat 参数,那就将断点先打到回调函数体内,如这里的第 20 行,通过调用堆栈去找 stat。在 JavaScript Debug Terminal 执行 node build.js,代码运行到断点处,这时不要着急,看左侧的调用堆栈。
(2)点击堆栈中第二行,到了 callback 的调用位置,明确目标,我们要找到 stats 是怎么传递的,于是关注到 callback 在 finalCallback 函数体内,这个函数接收到 stats 参数,所以要去找这个函数的调用位置;
(3)继续点击堆栈第三行,这时就来到调用 finalCallback 的位置,这里传递了 stats
(4)这时可以不用在堆栈找了,因为我们已经定位到位置,现在需要去搜索 stats 变量在哪里定义和赋值的
这里发现有一个 startTime,再去找它的声明和赋值的位置
(5)在(4)的分析中,有一个关键的位置 new Stats(compilation),可以跟进去看一下,ctrl+click
进到了 Stats.js 中,猜想是通过这两个方法拿到 startTime/endTime
(6)现在已经比较清晰了,可以在以上分析中的关键代码中打断点,然后执行,进去看看每个断点位置的执行情况
(7)还有一个简单的验证方式,直接在 20 行打断点,然后单步调试
这样直接进到 Stats.js 中
# 小结
实际上就是在开始编译和编译结束的时候,通过 Date.now() 获取对应的时间戳,放在了 Stats 对象上(其实还是通过 compilation 拿到的)
Compiler.js
// ...
const startTime = Date.now() // 获取开始编译时间
// ...
compilation.startTime = startTime;
compilation.endTime = Date.now() // 获取编译结束时间
// ...
2
3
4
5
6